home *** CD-ROM | disk | FTP | other *** search
- unit uBoids;
-
- interface
-
- uses
- Graphics, Classes,uTMovable;
-
- const
- RecommendedSpeed = 0.45;
- LowestSpeedAllowed = 0.5;
-
- type
- TObstacle = class;
-
- TBoid = class(TMovable)
- bSmashed : boolean;
- iTeamNumber : integer;
-
- DeltaDirToClosest : real;
- AbsDirToClosest : real;
- SQRDist : real;
- sDx,sDy : real;
- AbsDirToAvg : real;
- AvgSpeed : real;
- AvgDir : real;
-
- // Unique to every boid if they are to be extended,
- // but usually the same.
- MaxSpeedChange : real;
- SensorDistance : real;
-
- OptimalDistance : real;
- StayInCenter : real;
- TooClose : real;
- ReallyClose : real;
- MaxTurnSpeed : real;
-
- procedure AvoidObstacle(Canvas : TCanvas;ClosestObstacle : TObstacle);
- function AvoidBoid(ClosestBoid : TBoid) : boolean;
- procedure StayCentered;
- function PrepareToMove(ClosestBoids : TList; ClosestObstacle : TMovable; Canvas : TCanvas) : boolean ; override;
-
- procedure IncreaseSpeed(delta : real);override;
- procedure DecreaseSpeed(delta : real);override;
-
- procedure TurnLeft(delta : real);override;
- procedure TurnRight(delta : real);override;
- private
- TurnFraction : real;
- end;
-
- TObstacle = class(TMovable)
- Size : integer;
- AvoidSphere : real;
- procedure Draw(Canvas : TCanvas);override;
- function PrepareToMove(ClosestBoids : TList;ClosestObstacle : TMovable; Canvas : TCanvas) : boolean;override;
- // procedure Move(Canvas : TCanvas);override;
- constructor Create(inX, inY,inSize : Integer; inColor : TColor; Canvas : TCanvas);
- end;
-
- implementation
-
- //******************************************************************************
- procedure TBoid.TurnLeft(delta : real);
- begin
- Dir := Dir - Min(MaxTurnSpeed,abs(delta));
- end;
-
- //******************************************************************************
- procedure TBoid.TurnRight(delta : real);
- begin
- Dir := Dir + Min(MaxTurnSpeed,abs(delta));
- end;
-
- //******************************************************************************
- procedure TBoid.IncreaseSpeed(delta : real);
- begin
- // Increase is slower than decrease
- Speed := Speed + Min(MaxSpeedChange,delta) + Deviation(0.05);
- if Speed > 1 then speed := 1;
- if Color <> clGreen then
- Color := clBlue;
- bSpeedHasChanged := true;
- end;
-
- //******************************************************************************
- procedure TBoid.DecreaseSpeed(delta : real);
- begin
- // Increase is slower than decrease
- Speed := Speed - Min(MaxSpeedChange,delta) + Deviation(0.05);
- if Speed < LowestSpeedAllowed then Speed := LowestSpeedAllowed;
- if Color <> clGreen then
- Color := clRed;
- bSpeedHasChanged := true;
- end;
-
- //******************************************************************************
- procedure TBoid.AvoidObstacle(Canvas : TCanvas;ClosestObstacle : TObstacle);
- var
- ODistSQR : real;
- ODir : real;
- ClosestDistance : real;
- AvoidObstacleDistSQR : real;
- fPanicLevel : real;
- fTurnDir : real;
- begin
- if (ClosestObstacle <> nil) then
- begin
- AvoidObstacleDistSQR := ClosestObstacle.AvoidSphere;
-
- ODistSQR := sqr(x-ClosestObstacle.X) + sqr(y-ClosestObstacle.y);
-
- if (ODistSQR < AvoidObstacleDistSQR) then
- begin
- ODir := ConfineDirection(Dir-MyArcTan(ClosestObstacle.X-x,ClosestObstacle.y-y));
-
- ClosestDistance := abs(sin(ODir) * Sqrt(ODistSQR));
-
- if (abs(ODir) < pi/2) and (ClosestDistance < ClosestObstacle.Size) then
- begin
- //fPanicLevel := 1-ODistSQR/AvoidObstacleDistSQR;
- fPanicLevel := max(1-ODistSQR/AvoidObstacleDistSQR,
- 1 - ClosestDistance/(ClosestObstacle.Size/2));
-
- { Canvas.Pen.Color := clWhite;
- Canvas.MoveTo(trunc(x),trunc(y));
- Canvas.LineTo(trunc(x + cos(Dir-ODir)*ClosestDistance),
- trunc(y + sin(Dir-ODir)*ClosestDistance));
-
- Canvas.MoveTo(trunc(x),trunc(y));
- Canvas.LineTo(trunc(x + cos(Dir)*ClosestDistance),
- trunc(y + sin(Dir)*ClosestDistance));
-
- // }
-
- fTurnDir :=(pi-abs(ODir))*fPanicLevel;
-
- if ODir > 0 then
- TurnRight(fTurnDir)
- else
- TurnLeft(fTurnDir);
-
- Color := clGreen;
-
- // TurnFraction := 1 - fPanicLevel;
- TurnFraction := 0.1;
-
- if fPanicLevel > 0.4 then
- begin
- DecreaseSpeed(fPanicLevel/10);
- TurnFraction := 0.1;
- end;
-
- if fPanicLevel > 0.6 then
- TurnFraction := 0.0;
- end;
- end;
- end; //}
- end;
-
- function TBoid.AvoidBoid(ClosestBoid : TBoid) : boolean;
- begin
- AvoidBoid := false;
-
- if (ClosestBoid.DistanceSquared < sqr(TooClose)) then
- begin
- // Don't fly directly behind someone!
- //if abs(DeltaDir) < 0.02 then DeltaDir := 0.07;
- if abs(DeltaDirToClosest) < 0.02 then DeltaDirToClosest := 0.1;
-
- // Allow boids going the same direction to be closer
- if (DeltaDirToClosest < 0.09) and
- (ClosestBoid.DistanceSquared > sqr(ReallyClose)) then
- DeltaDirToClosest := 0;
-
- if DeltaDirToClosest <> 0 then
- begin
- bSmashed := true;
- AvoidBoid := true;
-
- if AbsDirToClosest < 0 then
- TurnRight(DeltaDirToClosest{*0.15}*TurnFraction)
- else
- TurnLeft(DeltaDirToClosest{*0.15}*TurnFraction);
-
- // Adjust speed!
- if Within(AbsDirToClosest,-Pi/2,0) and
- Within(DeltaDirToClosest,0,Pi) then // Beta2
- DecreaseSpeed(abs(ClosestBoid.Speed-Speed)+0.09);
-
- if Within(AbsDirToClosest,0,Pi/2) and
- Within(DeltaDirToClosest,-Pi,0) then
- DecreaseSpeed(abs(ClosestBoid.Speed-Speed)+0.09);
-
- // Overrules the next rule
- if (ClosestBoid.DistanceSquared < sqr(ReallyClose)) then
- TurnFraction := 0.01*TurnFraction;
- end;
- end;
- //}
- end;
-
- procedure TBoid.StayCentered;
- var
- NewAvgDir : real;
- LocalTurnFraction : real;
- begin
- if SqrDist > sqr(OptimalDistance-StayInCenter) then
- begin
- //3.Cohesion: steer to move toward the average position of local flockmates. / Craig Reynolds
- // We're too far off, stear not only in the same direciton,
- // but towards the center of gravity!
- NewAvgDir := MyArcTan(sDx-x,sDy-y);
-
- // Break if you're in front of the crowd,
- // speed up if you're behind it.
- if Abs(ConfineDirection(Dir-AbsDirToAvg)) < pi/2 then
- IncreaseSpeed(abs(AvgSpeed-Speed)+0.02)
- else
- DecreaseSpeed(abs(AvgSpeed-Speed)+0.02);
- end else NewAvgDir := AvgDir;
-
- if Dir < NewAvgDir then
- TurnRight(Abs(Dir - NewAvgDir)*TurnFraction)
- else
- TurnLeft(Abs(Dir - NewAvgDir)*TurnFraction);
- //} *)
- end;
-
- //******************************************************************************
- function TBoid.PrepareToMove(ClosestBoids : TList; ClosestObstacle : TMovable; Canvas : TCanvas) : boolean;
- var
- i : integer;
- ClosestBoid : TBoid;
- RelativeDirection : real;
- dx,dy : real;
-
- bNeighboursFound : boolean;
-
- RelDirToAvg : real;
- iTeamCount : integer;
- LastMoveXSum : real;
- LastMoveYSum : real;
- TestBoid : TBoid;
- begin
- // First, collect data
- Color := clWhite;
- bSpeedHasChanged := false;
- bSmashed := false;
- iTeamCount := 0;
-
- PrepareToMove := false;
-
- sDx := 0;
- sDy := 0;
- AvgSpeed := 0;
- AvgDir := 0;
-
- bNeighboursFound := ClosestBoids.Count <> 0;
-
- if bNeighboursFound then
- ClosestBoid := ClosestBoids[0]
- else
- ClosestBoid := nil;
-
- for i := 0 to ClosestBoids.Count - 1 do
- begin
- TestBoid := ClosestBoids[i];
- AvgSpeed := AvgSpeed + TestBoid.Speed;
- AvgDir := AvgDir + TestBoid.Dir;
- sDx := sDx + TestBoid.X;
- sDy := sDy + TestBoid.Y;
- LastMoveXSum := LastMoveXSum + TestBoid.LastMoveX;
- LastMoveYSum := LastMoveYSum + TestBoid.LastMoveY;
-
- if TestBoid.DistanceSquared < ClosestBoid.DistanceSquared then
- ClosestBoid := ClosestBoids[i];
- end;
-
- if bNeighboursFound then
- begin
- AvgSpeed := AvgSpeed / ClosestBoids.Count;
-
- AvgDir := AvgDir / ClosestBoids.Count{ + (random(100)-50)/5000;//};
- // Calculate the average heading of the surrounding flock, including
- // the boid itself
- {AvgDir := MyArcTan(LastMoveXSum+LastMoveX*ClosestBoids.Count/2,
- LastMoveYSum+LastMoveY*ClosestBoids.Count/2);}
- sDx := sDx / ClosestBoids.Count;
- sDy := sDy / ClosestBoids.Count;
-
- dx := ClosestBoid.X-x;
- dy := ClosestBoid.Y-y;
-
- AbsDirToClosest := MyArcTan(dx,dy);
- AbsDirToAvg := MyArcTan(sDx-x,sDy-y);
- DeltaDirToClosest := ConfineDirection(abs(ClosestBoid.Dir-Dir));
- end
- else
- begin
- AvgSpeed := RecommendedSpeed;
- AvgDir := Dir;
- AbsDirToClosest := dir;
- sDx := 0;
- sDy := 0;
- DeltaDirToClosest := 0;
- end;
-
- SQRDist := sqr(sDx-x)+sqr(sDy-y);
- TurnFraction := 1.0;
-
- // Craig Reynold's three rules of flocking are;
- // 1.Separation: steer to avoid crowding local flockmates.
- // 2.Alignment: steer towards the average heading of local flockmates.
- // 3.Cohesion: steer to move toward the average position of local flockmates.
-
- // Rule zero is just to spice it up!
-
- // * Rule zero; don't hit the obstacle!
- AvoidObstacle(Canvas,TObstacle(ClosestObstacle));
-
- // 1.Separation: steer to avoid crowding local flockmates. / Craig Reynolds
- if bNeighboursFound then
- AvoidBoid(ClosestBoid);
-
- //2.Alignment: steer towards the average heading of local flockmates. / Craig Reynolds
- // and
- //3.Cohesion: steer to move toward the average position of local flockmates.
- if bNeighboursFound then
- StayCentered;
- end;
-
- //******************************************************************************
- procedure TObstacle.Draw(Canvas : TCanvas);
- var
- cX, cy : integer;
- hSize : integer;
- begin
- if not bActive then exit;
- cx := trunc(x);
- cy := trunc(y);
- hSize := Size div 2;
- Canvas.Brush.Color := Color;
- Canvas.Pen.Color := Color;
- Canvas.Ellipse(Cx - hSize, Cy - hSize,Cx + hSize, Cy + hSize);
- Canvas.Brush.Color := clWhite;
- end;
-
- //******************************************************************************
- function TObstacle.PrepareToMove(ClosestBoids : TList;ClosestObstacle : TMovable; Canvas : TCanvas):boolean;
- begin
- end;
-
- //******************************************************************************
- constructor TObstacle.Create(inX, inY,inSize : Integer; inColor : TColor; Canvas : TCanvas);
- begin
- inherited Create(Canvas);
- X := inX;
- Y := inY;
- Speed := 0;
- Size := inSize;
- AvoidSphere := sqr(inSize*3);
- Color := inColor;
- bActive := true;
- end;
- end.
-